#pragma once

#include <windows.h>
#include <cstdio>

template <class DERIVED_TYPE>
class BaseWindow
{
public:
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        DERIVED_TYPE* pThis = nullptr;

        if (uMsg == WM_NCCREATE)
        {
            CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
            pThis = static_cast<DERIVED_TYPE*>(pCreate->lpCreateParams);
            SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
            
            pThis->m_hwnd = hwnd;
        }
        else
        {
            pThis = reinterpret_cast<DERIVED_TYPE*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
        }

        return pThis ? pThis->HandleMessage(uMsg, wParam, lParam) : DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    BaseWindow() : m_hwnd(nullptr) { }
    virtual ~BaseWindow() = default;

    BOOL Create(
        PCWSTR lpWindowName,
        DWORD dwStyle,
        DWORD dwExStyle = 0,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        int nWidth = 800,
        int nHeight = 480,
        HWND hWndParent = NULL,
        HMENU hMenu = NULL)
    {
        if (!RegisterWindowClass())
            return FALSE;

        RECT rcClient = {0, 0, nWidth, nHeight}; // 期望的客户区尺寸
        AdjustWindowRectEx(&rcClient, WS_OVERLAPPEDWINDOW, FALSE, 0);
    
        int windowWidth = rcClient.right - rcClient.left;
        int windowHeight = rcClient.bottom - rcClient.top;       
        m_hwnd = CreateWindowExW(
            dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
            windowWidth, windowHeight, hWndParent, hMenu, GetModuleHandle(nullptr), this);

        return m_hwnd ? TRUE : FALSE;
    }

    HWND Window() const { return m_hwnd; }

protected:
    virtual LPCWSTR ClassName() const = 0;
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;

    BOOL RegisterWindowClass()
    {
        static bool registered = false;
        if (!registered)
        {
            WNDCLASSEXW wc = { sizeof(WNDCLASSEXW) };
            wc.style         = CS_HREDRAW | CS_VREDRAW;
            wc.lpfnWndProc   = DERIVED_TYPE::WindowProc;
            wc.hInstance     = GetModuleHandle(nullptr);
            wc.hCursor       = LoadCursor(nullptr, IDC_ARROW);
            wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
            wc.lpszClassName = ClassName();

            if (!RegisterClassExW(&wc))
                return FALSE;

            registered = true;
        }
        return TRUE;
    }

    HWND m_hwnd;
};